﻿using System;

public class Mapper018 : MapperDefault
{
    private int irq_counter;
    private bool irq_enabled;
    private int irq_latch;
    internal int num_8k_banks;
    internal int patch;
    private int[] regs = new int[11];

    public override void init(NES nes)
    {
        base.init(nes);
        this.reset();
    }

    public override void loadROM(ROM rom)
    {
        if (!rom.Valid)
        {
            Console.Out.WriteLine("VRC2: Invalid ROM! Unable to load.");
        }
        else
        {
            int num = rom.RomBankCount * 2;
            this.load8kRomBank(0, 0x8000);
            this.load8kRomBank(1, 0xa000);
            this.load8kRomBank(num - 2, 0xc000);
            this.load8kRomBank(num - 1, 0xe000);
            this.loadCHRROM();
            this.loadBatteryRam();
            base.nes.Cpu.requestIrq(2);
        }
    }

    public override void mapperInternalStateLoad(ByteBuffer buf)
    {
        base.mapperInternalStateLoad(buf);
        if (buf.readByte() == 1)
        {
            this.irq_counter = buf.readInt();
            this.irq_latch = buf.readInt();
            this.irq_enabled = buf.readBoolean();
        }
    }

    public override void mapperInternalStateSave(ByteBuffer buf)
    {
        base.mapperInternalStateLoad(buf);
        buf.putByte(1);
        buf.putInt(this.irq_counter);
        buf.putInt(this.irq_latch);
        buf.putBoolean(this.irq_enabled);
    }

    public override void reset()
    {
        this.regs[0] = 0;
        this.regs[1] = 1;
        this.regs[2] = this.num_8k_banks - 2;
        this.regs[3] = this.num_8k_banks - 1;
        this.regs[4] = 0;
        this.regs[5] = 0;
        this.regs[6] = 0;
        this.regs[7] = 0;
        this.regs[8] = 0;
        this.regs[9] = 0;
        this.regs[10] = 0;
        this.irq_enabled = false;
        this.irq_latch = 0;
        this.irq_counter = 0;
    }

    public override int syncH(int scanline)
    {
        if (this.irq_enabled)
        {
            if (this.irq_counter <= 0x71)
            {
                this.irq_counter = (this.patch == 1) ? 0x72 : 0;
                this.irq_enabled = false;
                return 3;
            }
            this.irq_counter -= 0x71;
        }
        return 0;
    }

    public override void write(int address, short value_Renamed)
    {
        if (address < 0x8000)
        {
            base.write(address, value_Renamed);
        }
        else
        {
            switch (address)
            {
                case 0xe000:
                    this.irq_latch = (this.irq_latch & 0xfff0) | (value_Renamed & 15);
                    return;

                case 0xe001:
                    this.irq_latch = (this.irq_latch & 0xff0f) | ((value_Renamed & 15) << 4);
                    return;

                case 0xe002:
                    this.irq_latch = (this.irq_latch & 0xf0ff) | ((value_Renamed & 15) << 8);
                    return;

                case 0xe003:
                    this.irq_latch = (this.irq_latch & 0xfff) | ((value_Renamed & 15) << 12);
                    return;

                case 0xf000:
                    this.irq_counter = this.irq_latch;
                    return;

                case 0xf001:
                    this.irq_enabled = (value_Renamed & 1) != 0;
                    return;

                case 0xf002:
                    value_Renamed = (short) (value_Renamed & 3);
                    if (value_Renamed != 0)
                    {
                        if (value_Renamed == 1)
                        {
                            base.nes.Ppu.Mirroring = 0;
                        }
                        else
                        {
                            base.nes.Ppu.Mirroring = 3;
                        }
                        return;
                    }
                    base.nes.Ppu.Mirroring = 1;
                    return;

                case 0xc000:
                    this.regs[7] = (this.regs[7] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[7], 0x1000);
                    return;

                case 0xc001:
                    this.regs[7] = (this.regs[7] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[7], 0x1000);
                    return;

                case 0xc002:
                    this.regs[8] = (this.regs[8] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[8], 0x1400);
                    return;

                case 0xc003:
                    this.regs[8] = (this.regs[8] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[8], 0x1400);
                    return;

                case 0xd000:
                    this.regs[9] = (this.regs[9] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[9], 0x1800);
                    return;

                case 0xd001:
                    this.regs[9] = (this.regs[9] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[9], 0x1800);
                    return;

                case 0xd002:
                    this.regs[10] = (this.regs[10] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[10], 0x1c00);
                    return;

                case 0xd003:
                    this.regs[10] = (this.regs[10] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[10], 0x1c00);
                    return;

                case 0xa000:
                    this.regs[3] = (this.regs[3] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[3], 0);
                    return;

                case 0xa001:
                    this.regs[3] = (this.regs[3] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[3], 0);
                    return;

                case 0xa002:
                    this.regs[4] = (this.regs[4] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[4], 0x400);
                    return;

                case 0xa003:
                    this.regs[4] = (this.regs[4] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[4], 0x400);
                    return;

                case 0xb000:
                    this.regs[5] = (this.regs[5] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[5], 0x800);
                    return;

                case 0xb001:
                    this.regs[5] = (this.regs[5] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[5], 0x800);
                    return;

                case 0xb002:
                    this.regs[6] = (this.regs[6] & 240) | (value_Renamed & 15);
                    this.load1kVromBank(this.regs[6], 0xc00);
                    return;

                case 0xb003:
                    this.regs[6] = (this.regs[6] & 15) | ((value_Renamed & 15) << 4);
                    this.load1kVromBank(this.regs[6], 0xc00);
                    return;

                case 0x8000:
                    this.regs[0] = (this.regs[0] & 240) | (value_Renamed & 15);
                    this.load8kRomBank(this.regs[0], 0x8000);
                    return;

                case 0x8001:
                    this.regs[0] = (this.regs[0] & 15) | ((value_Renamed & 15) << 4);
                    this.load8kRomBank(this.regs[0], 0x8000);
                    return;

                case 0x8002:
                    this.regs[1] = (this.regs[1] & 240) | (value_Renamed & 15);
                    this.load8kRomBank(this.regs[1], 0xa000);
                    return;

                case 0x8003:
                    this.regs[1] = (this.regs[1] & 15) | ((value_Renamed & 15) << 4);
                    this.load8kRomBank(this.regs[1], 0xa000);
                    return;

                case 0x9000:
                    this.regs[2] = (this.regs[2] & 240) | (value_Renamed & 15);
                    this.load8kRomBank(this.regs[2], 0xc000);
                    return;

                case 0x9001:
                    this.regs[2] = (this.regs[2] & 15) | ((value_Renamed & 15) << 4);
                    this.load8kRomBank(this.regs[2], 0xc000);
                    return;
            }
        }
    }

    public override long CRC
    {
        set
        {
            this.patch = 0;
            if (value == 0xf1babe7L)
            {
                this.patch = 1;
                Console.Out.WriteLine("Patched CRC:" + value);
            }
        }
    }
}

